import libtcodpy as libtcod
import config

import ent, player, ent_furniture
import ai, events, combat, items

import map_tile, map_block, mapgen # viewport #?


from sk_math import Point

import random, sys, time

color_dark_wall = libtcod.Color(0, 0, 100)
color_light_wall = libtcod.Color(130, 110, 50)
color_dark_ground = libtcod.Color(50, 50, 150)
color_light_ground = libtcod.Color(200, 180, 50)

ROAD_SIZE = 3
MIN_BLOCK_SIZE = 2000

MAP_DEBUG = False
if config.__PYSKILLER_DEBUG:
	MAP_DEBUG = True
	
MAP_SEED = libtcod.random_get_int(0,0,999999)	
if config.__PYSKILLER_SEED:
	MAP_SEED = config.__PYSKILLER_SEED 
	
if MAP_DEBUG:
	CHUNK_SIZE = 60
else:
	CHUNK_SIZE = 60

	
#-----------------------------------------------------------------------		
import names
import bodysim		
#-----------------------------------------------------------------------

#=======================================================================

class Chunk(map_block.Block):	
	def __init__(self, em, x, y):

		super(Chunk ,self).__init__( x, y, CHUNK_SIZE, CHUNK_SIZE )
			
		self.em = em
		
		self.loaded = False
		
		#chunk_seed_gen = libtcod.random_new_from_seed(MAP_SEED)
		self.seed = libtcod.random_get_int(self.em.services["map"].generator,0,999999)
		
		#self.generator = libtcod.random_new_from_seed(self.seed)
		
	def coord2world(self,i,j):
		x = self.x*CHUNK_SIZE + i
		y = self.y*CHUNK_SIZE + j
		
		return (x,y)

	def LoadChunk(self):
		if self.loaded:
			return
			
		map = self.em.services["map"]	
		for i in xrange(CHUNK_SIZE):
			for j in xrange(CHUNK_SIZE):
				(x,y) = self.coord2world(i,j)
				map.tiles[(x,y)] = map_tile.Tile(False)
				
		self.loaded = True
			
	def UnloadChunk(self):
		if not self.loaded:
			return
			
		for i in xrange(CHUNK_SIZE):
			for j in xrange(CHUNK_SIZE):
				(x,y) = self.coord2world(i,j)
				#TODO: unload tiles, unload objects, so on
				
		self.loaded = False

#=======================================================================
class Map(events.EventListener):
	def __init__(self, w, h):
		self.w = w
		self.h = h
		
		self.em = None
		
		self.namegen = names.NameGen(MAP_SEED)			
		self.graph = {}
		
		self.generator = libtcod.random_new_from_seed(MAP_SEED)

	#todo: remove debug 'con'
	def build_map(self, con):
		
		print "Starting world generation with seed '"+str(MAP_SEED)+"'"
		
		
		self.em.register("sk_map_apt_rooms",{})
		self.em.register("sk_map_apt_beds",{})
		
		self.em.register("sk_map_npc",[])
		self.em.register("milestones",[])
	
		self.tiles = {}		
		
		self.chunks = {}
		for i in xrange(-5,5):
			for j in xrange(-5,5):
				self.chunks[(i,j)] = Chunk(self.em,i,j)
				
		#init primary tile array
		
		#for i in xrange(-2,2):
		#	for j in xrange(-2,2):
		#		self.chunks[(i,j)].LoadChunk()
		self.RebuildChunks(0,0)

		
		#self.BuildChunk(con, self.chunks[(0,0)])
		#self.BuildChunk(con, self.chunks[(0,1)])
		#self.BuildChunk(con, self.chunks[(1,1)])
		#self.BuildChunk(con, self.chunks[(1,0)])
		
	def RebuildChunks(self, chunk_x, chunk_y):
		#rebuild 3x3 square around chunk_x, chunk_y
		
		for i in xrange(chunk_x-1,chunk_x+2):
			for j in xrange(chunk_y-1,chunk_y+2):
				if self.chunks.has_key((i,j)):
					print "loading chunk (%d,%d)" % (i,j)
					self.chunks[(i,j)].LoadChunk()
					self.BuildChunk(None, self.chunks[(i,j)])
		

	def BuildChunk(self, con, chunk):
		#---------------------------------------------------------------
		#if not MAP_DEBUG:
		#	game_block = map_block.Block(ROAD_SIZE + chunk.x*CHUNK_SIZE ,ROAD_SIZE + chunk.y*CHUNK_SIZE ,self.w-ROAD_SIZE*2,self.h-ROAD_SIZE*2)
		#else:
		
		game_block = map_block.Block(
			ROAD_SIZE + chunk.x*CHUNK_SIZE,
			ROAD_SIZE + chunk.y*CHUNK_SIZE,
			CHUNK_SIZE - ROAD_SIZE*2 ,
			CHUNK_SIZE - ROAD_SIZE*2
		)
			
		self.em.register("sk_map_apartments",[])	#erase this container
			

		ts_start = time.time()
		#---------------------------------------------------------------
		
		#seed = MAP_SEED
		
		print "Starting chunk generation with seed '"+str(chunk.seed)+"'"
		
		gen = mapgen.MapGenerator(game_block)
		gen.set_seed(chunk.seed)
		gen.con = con
			
		districts = gen.Process([game_block])
		if isinstance(districts,map_block.Block):
			#TODO: throw exception there!
			districts = [districts]
		
		self.em.register("sk_map_districts",districts)
		
		milestones = self.em.services["milestones"]
		
		
		
		if len(districts):
			for district in districts:
								#create milestones before scaling
				for ms in ( 
					(district.x, district.y),
					(district.x, district.y+district.h),
					(district.x+district.w, district.y),
					(district.x+district.w, district.y+district.h)
				):
					if not ms in milestones:
						milestones.append(Point(ms[0],ms[1]))
						
			roads = []
			self.em.register("roads",roads)
						
			for district in districts:			
				#wrap district block in a roads	
				self.GenerateRoads(district)
				
				district.scale(-ROAD_SIZE,-ROAD_SIZE)
			
			#-----------------------------------------------------------
			#		randomly place safehouse
			#-----------------------------------------------------------	
			safehouse_block = districts[libtcod.random_get_int(self.generator, 0, len(districts)-1)]
			self.GenerateSafehosue(safehouse_block)
			
			districts.remove(safehouse_block)
			
			#-----------------------------------------------------------
			#		create other housing areas
			#-----------------------------------------------------------
			for district in districts:
				self.FillBlock(district)

		#self.em.register("milestones",milestones)
		
		#DEBUG DEBUG DEBUG
		objects = self.em.services["objects"]
		for ms in milestones:
			node = ent.Entity(self.em, ms.x, ms.y, 'N', libtcod.blue)
			node.name = "MS NODE"
			objects.append(node)
		#DEBUG END
		#---------------------------------------------------------------
		
		
		self.PopulateMap()
		
		for apt in self.em.services["sk_map_apartments"]:
			print "filling room"
			self.AptFillRooms(apt)
		
		
		#---------------------------------------------------------------
		ts_end = time.time()
		print "chunk generated in %f seconds" % (ts_end-ts_start)

	def AptFillRooms(self, apt):
		#INSIDE OF ROOM - PLACE FURNITURE
		rooms = self.em.services["sk_map_apt_rooms"][apt]
		if len(rooms) == 0:
			return
		
		#one kitchen
		kitchen =  rooms[libtcod.random_get_int(self.generator, 0, len(rooms)-1)]
		self.FillRoom(apt, kitchen, 1)
		rooms.remove(kitchen)
			
		#one bathroom
		bathroom =  rooms[libtcod.random_get_int(self.generator, 0, len(rooms)-1)]
		self.FillRoom(apt, bathroom, 2)
		rooms.remove(bathroom)

		sample_tile = self.tiles[(apt.x+1,apt.y+1)]
		
		#some bedrooms
		
		self.em.services["sk_map_apt_beds"][apt] = []
		
		owner_count = 1	#at least one owner
		if sample_tile.owners:
			owner_count = len(sample_tile.owners)
			
		#print str(owner_count) + " owners registered there"

		for i in xrange(owner_count):
			if rooms:
				bedroom =  rooms[libtcod.random_get_int(self.generator, 0, len(rooms)-1)]
				self.FillRoom(apt, bedroom, 0)
				#rooms.remove(bedroom)	//allow to place 2 or more beds
		
		#TODO: fill rest rooms there
			
					
		#self.em.services["render"].message("rooms generated:" + str(len(rooms)))

	#FILL MAP WITH NPC
	#this method should be called BEFORE room structure generation!
	
	def PopulateMap(self):
		
		#place pedestrian on a road
		for road in self.em.services["roads"]:
			
				#npc_count = libtcod.random_get_int(self.generator,0,1)
				#lil fix
				npc_count = 0
				if libtcod.random_get_int(self.generator,0,100) < 50:
					npc_count = 1
				
				for i in xrange(npc_count):
					(x,y) = self.get_rand_coord(road)
							
					npc = self.place_npc(x,y)
					npc.set_controller(ai.PedestrianAI())

							
					#select random APT and mark it as npc's home
					#so it is not being homeless
							
					apt = self.em.services["sk_map_apartments"]
					if apt:
						random_apt = apt[libtcod.random_get_int(self.generator,0,len(apt)-1)]
						
						npc.apt = random_apt	#set apartment for this npc to provide fast entity search inside of apt area
						#this pointer is primary used for bedroom location for now
								
						for n in range(random_apt.x,random_apt.x+random_apt.w):
							for m in range(random_apt.y,random_apt.y+random_apt.h):
								self.tiles[(n,m)].owners.append(npc)
		#===========================================================
		
		#-------------------------------------------------------
		#police start
		for z in range(2):	#4 policeman
			objects = self.em.services["objects"]
			roads = self.em.services["roads"]
			
			road = roads[libtcod.random_get_int(self.generator, 0, len(roads)-1)]
			(x,y) = self.get_rand_coord(road)
							
							
			police = ent.Human(self.em, x, y, 'P', libtcod.dark_blue)
			police.name = "Police man"

			police.set_controller(ai.PoliceAI())
			police.set_container(ent.NPCContainer())
			police.set_combat(combat.ActorCombat())
			police.set_body(bodysim.BodySimulation())
			self.add_npc(police)
				
			#----------
			vest = items.Item("Bulletproof vest")
			vest.slot = "armor"
			vest.effects["defence"] = 60	#60% damage resistance		
			vest.pick_up(police)
					
			police.container.equip(vest) 	#by default
			#----------
			stunstick = items.Item("Stunstick")
			stunstick.slot = "weapon"
			stunstick.effects["stun_chance"] = 40
			stunstick.effects["damage"] = 5
			stunstick.effects["damage_type"] = "dmg_blunt"
			
			police.container.equip(stunstick)
			
				
		#police end
		#---------------------------------------------------------------
		
			
	def get_objects(self,x,y):
		'''objects = []
		for obj in self.em.services["objects"]:
			if obj.x == x and obj.y == y:
				objects.append(obj)'''
				
				
		return self.get_tile(x,y).objects	
			
	def get_obstacle(self,x,y):
		'''for obj in self.em.services["objects"]:
			if obj.obstacle and obj.x == x and obj.y == y:
				return obj
				#TODO: make a list and return it instead of single object'''
		tile = self.get_tile(x,y)
		if tile and len(tile.objects) and tile.objects[0].obstacle:
			return tile.objects[0]
			
		return None
	
	def is_blocked(self,x,y):
		
		if not self.tiles.has_key((x,y)):
			return True
		tile = self.tiles[(x,y)]
		
		if tile.blocked or tile.obstacle:
			return True
		
		#for obj in self.
		
		#TODO: replace with 'if tile.obstacle'
		#if tile.obstacle
		#for obj in self.em.services["objects"]:
			#if obj.obstacle and obj.x == x and obj.y == y:
				#return True
						
		return False
		
	#-------------------------------------------------------------------
	def FillBlock(self,block):
		if libtcod.random_get_int(self.generator, 0, 100) > 20:
			#self.GenerateHousing(block)
			self.GenerateAptHousing(block)
		else:
			self.GeneratePark(block)
			
	#===================================================================
	#generate apt area
	#===================================================================
	def GenerateHousing(self,block):
		objects = self.em.services["objects"]

		#bug there >>>>>
		#commenting following lines will exploit incorrect room wall placement
		
		for i in range(block.h+1):
			self.PlaceWall(block.x,block.y+i)
			self.PlaceWall(block.x+block.w,block.y+i)
			
		for j in range(block.w+1):
			self.PlaceWall(block.x+j,block.y)
			self.PlaceWall(block.x+j,block.y+block.h)
			
		#<<< bug there
		
		#make rooms in the housing (jist like districts, xcept lower nestness
		#ROOM_COUNT = 5	<<ref
		ROOM_COUNT = 4
		
		
		gen = mapgen.MapGenerator(block)
		gen.MIN_BLOCK_SIZE = int(block.size() / ROOM_COUNT)
		rooms = gen.RoomProcess([block])

		
		for room in rooms:
			for i in range(room.h+1):
				self.PlaceWall(room.x,room.y+i)
				self.PlaceWall(room.x+room.w,room.y+i)
			for j in range(room.w+1):
				self.PlaceWall(room.x+j,room.y)
				self.PlaceWall(room.x+j,room.y+room.h)
				
		#make doors
		#super-idiotic approach
		#print "starting room generation:"
		
		for room in rooms:
			for room2 in rooms:
				if room is not room2:
					if room.intersect(room2):
						if not room.connected(room2) and not room2.connected(room):
							#print "detected intersection of room #" + str(rooms.index(room)) + " and room #" + str(rooms.index(room2)) 
							intrs = room.get_intersection(room2)
							if intrs:
								#~ for i in range(intrs.w):
									#~ for j in range(intrs.h):
										#~ X = ent.Entity(self.em, intrs.x+i, intrs.y+j, 'X', libtcod.red)
										#~ X.name = "WALL"
										#~ objects.append(X)
							
								wall = intrs.to_range()
								#print "wall size is "+str(len(wall))
								if len(wall) > 2:
						
									door_coord = wall[libtcod.random_get_int(self.generator, 1, len(wall)-2)]
									
									self.ClearWall(door_coord[0], door_coord[1])
									
									#~ door = ent.Furniture(self.em, door_coord[0], door_coord[1], '+', libtcod.green)
									door = ent_furniture.Door(self.em, door_coord[0], door_coord[1], '+', libtcod.green)
									door.name = "door"
									door.hp = 5
									
									door.unlock()	#internal doors are unlocked
									#TODO: add small percent of locked internal doors
									
									objects.append(door)
									#self.tiles[door_coord[0]][door_coord[1]].block_sight = True
									door.spawn()
									
									room.neighbours.append(room2)	
						else:
							pass
			#room counter
			for outer_wall in room.get_outer_wall(block):
				rnd_char = str(libtcod.random_get_int(self.generator, 0, 9))
				if outer_wall:
					window_coord = outer_wall[int(len(outer_wall)/2)]	
					if libtcod.random_get_int(self.generator, 0, 100) > 20:
						window = ent_furniture.Furniture(self.em, window_coord[0], window_coord[1], '=', libtcod.white)
						window.name = "window"
						window.obstacle = True
						window.hp = 1
						objects.append(window)
						self.ClearWall(window_coord[0], window_coord[1])
						window.spawn()
					else:			
						door = ent_furniture.Door(self.em, window_coord[0], window_coord[1], '+', libtcod.green)
						door.name = "door"
						door.lock()
						door.hp = 5
						objects.append(door)
						
						self.tiles[(window_coord[0],window_coord[1])].blocked 		= False
						self.tiles[(window_coord[0],window_coord[1])].block_sight 	= True

						door.spawn()

			
		#store apt block AND rooms		
		self.em.services["sk_map_apartments"].append(block)
		self.em.services["sk_map_apt_rooms"][block] = rooms
		
	#HOUSING APPENDIX - SET ROOM TYPE AND FILL IT WITH FURNITURE
	def get_rand_coord(self,block):
		while(True):
			x = libtcod.random_get_int(self.generator, block.x+1, block.x+block.w-1)
			y = libtcod.random_get_int(self.generator, block.y+1, block.y+block.h-1)
			
			if not self.is_blocked(x,y):
				return (x,y)
				
	def get_furniture_coord(self,block):
		pass
		
	#-------------------------------------------------------------------	
	#Fill room with the furniture
	#-------------------------------------------------------------------
	def FillRoom(self, apt, room, room_type = None):
		objects = self.em.services["objects"]
		if room_type == None:
			room_type = libtcod.random_get_int(self.generator, 0, 5)
		
		if room_type == 0:	#bedroom
			(x,y) = self.get_rand_coord(room)
			bed = ent_furniture.Furniture(self.em, x, y, 'B', libtcod.white)
			bed.name = "Bed"
			bed.obstacle = False	#allow to stay on bed tile
			bed.hp = 100
			objects.append(bed)
			bed.spawn()
			
			apt_bed_list = self.em.services["sk_map_apt_beds"][apt]
			apt_bed_list.append(bed)
			
		elif room_type == 1:	#kitchen
			#-----------------------------------------------------------
			(x,y) = self.get_rand_coord(room)
			table = ent_furniture.Furniture(self.em, x, y, 'T', libtcod.white)
			table.name = "Table"
			table.obstacle = True
			table.hp = 100
			objects.append(table)
			table.spawn()
			#-----------------------------------------------------------
			(x,y) = self.get_rand_coord(room)
			refr = ent_furniture.Refrigirator(self.em, x, y, libtcod.white)
			
			objects.append(refr)
			refr.spawn()
		elif room_type == 2: #bathroom
			(x,y) = self.get_rand_coord(room)
			sink = ent_furniture.Sink(self.em, x, y, 'S', libtcod.white)
			sink.name = "Sink"
			sink.obstacle = True
			sink.hp = 100
			objects.append(sink)
			sink.spawn()
			#add cleanup command
			
			#-----------------------------------------------------------
			(x,y) = self.get_rand_coord(room)
			cabinet = ent_furniture.Furniture(self.em, x, y, 'C', libtcod.white)
			cabinet.name = "Cabinet"
			cabinet.obstacle = True
			cabinet.hp = 100
			objects.append(cabinet)
			cabinet.spawn()
			
			cabinet.set_container(ent.BaseContainer())
			
			def healthkit_use(caller):
				caller.body.heal(50)
			
			healthkit = items.Item("healthkit")
			healthkit.use_action = healthkit_use
			healthkit.pick_up(cabinet)
			
			def bandages_use(caller):
				caller.body.bleeding = False
				
			bandages = items.Item("bandages")
			bandages.use_action = bandages_use
			bandages.pick_up(cabinet)
			#-----------------------------------------------------------
			
			
	#-------------------------------------------------------------------
	#	Generate house and populate it with npc
	#-------------------------------------------------------------------
	def GenerateAptHousing(self,block):
		self.GenerateHousing(block)
		
		'''
		MAX_NPC = 3
			
		num_npc = libtcod.random_get_int(0, 0, MAX_NPC)
		
		for i in range(num_npc):
			x = libtcod.random_get_int(0, block.x, block.x+block.w)
			y = libtcod.random_get_int(0, block.y, block.y+block.h)
			
			
			if not self.is_blocked(x,y):
				
				npc = self.place_npc(x,y)
				
				for n in range(block.x,block.x+block.w):
					for m in range(block.y,block.y+block.h):
						self.tiles[n][m].owners.append(npc)	#rightous owner of this house
		'''
	
	#-------------------------------------------------------------------
	#	Generate player's safehouse
	#-------------------------------------------------------------------
	def GenerateSafehosue(self,block):

		self.GenerateHousing(block)
		self.em.services["sk_map_apartments"].remove(block)	#no one dares to live in my house!
		
		for i in range(block.w+1):
			for j in range(block.h+1):
				tile = self.tiles[(block.x+i,block.y+j)]
				tile.explored = True
				
		#store safehouse to place player there later		
		self.safehouse_block = block
		
		self.AptFillRooms(block)
		
		#proffit
		#TODO: create lovely family
		
	#-------------------------------------------------------------------	
	def place_player(self,player):
	
		block = self.safehouse_block

	
		#place our player somewhere there		
		while(True):
			x = libtcod.random_get_int(self.generator, block.x, block.x+block.w)
			y = libtcod.random_get_int(self.generator, block.y, block.y+block.h)
			
			if not self.is_blocked(x,y):
				player.moveto(x,y)
				break
	#-------------------------------------------------------------------	
	def place_npc(self,x,y):
		objects = self.em.services["objects"]
		
		npc = ent.Human(self.em, x, y, '@', libtcod.darker_green)
		npc.sex = ("male","female")[libtcod.random_get_int(self.generator, 0, 1)]
		
		#todo: add pregnancy there
		#todo: add optional birthgiving
		
		npc.name = self.namegen.generate(npc.sex)
		npc.age = libtcod.random_get_int(self.generator, 8, 95)
		npc.race = ("white", "black", "hispanic", "asian")[libtcod.random_get_int(self.generator, 0, 3)]
		
		#based on manhatten religious statistic
		i = libtcod.random_get_int(self.generator, 0, 100)
		if i > 65:
			npc.religion = "catholic"
		elif i > 45:
			npc.religion = "jewish"
		elif i > 25:
			npc.religion = "protestant"
		elif i > 20:
			npc.religion = "muslim"
		else:
			npc.religion = "atheist"
			
				
		base_ai = ai.BaseAI()
		base_combat  = combat.ActorCombat()
		body = bodysim.BodySimulation()
		
		npc.set_controller(base_ai)
		npc.set_combat(base_combat)

		npc.set_body(body)
		self.add_npc(npc)
		
		return npc
		
		
	#simple wrapper for npc placement	
	def add_npc(self,npc):
		self.em.services["objects"].append(npc)
		self.em.services["sk_map_npc"].append(npc)
		
		npc.spawn()	#just in case
		
	#-------------------------------------------------------------------	
	#generate park area using current block	
	#-------------------------------------------------------------------
	
	def get_tile(self,x,y):
		if self.tiles.has_key((x,y)):
			return self.tiles[(x,y)]
					
	def GeneratePark(self, block):
		objects = self.em.services["objects"]
		
		for i in range(block.w+1):
			for j in range(block.h+1):
				self.tiles[(block.x+i,block.y+j)].model = libtcod.CHAR_BLOCK3
				self.tiles[(block.x+i,block.y+j)].color = libtcod.green
		#BUG: block_sight not working properly
		#debug only-----------------------------------------------------
		for i in range(block.h+1):
			self.tiles[(block.x,block.y+i)].model = libtcod.CHAR_BLOCK2
			self.tiles[(block.x+block.w,block.y+i)].model = libtcod.CHAR_BLOCK2
			
		for j in range(block.w+1):
			self.tiles[(block.x+j,block.y)].model = libtcod.CHAR_BLOCK2
			self.tiles[(block.x+j,block.y+block.h)].model = libtcod.CHAR_BLOCK2
		#---------------------------------------------------------------
		
		
		for i in range(block.w+1):
			for j in range(block.h+1):
				
				if libtcod.random_get_int(self.generator, 0, 200) < 1:
					self.place_npc(block.x+i, block.y+j)
					
				if libtcod.random_get_int(self.generator, 0, 100) < 2:
					tree = ent.Entity(self.em, block.x+i, block.y+j, 'T', libtcod.darker_green)
					tree.name = "tree"
					#tree.obstacle = True
					objects.append(tree)
						
				elif libtcod.random_get_int(self.generator, 0, 100) < 15:
					tree = ent.Entity(self.em, block.x+i, block.y+j, '"', libtcod.darker_green)
					tree.name = "grass"
					objects.append(tree)			
		pass
	#-------------------------------------------------------------------	
	def GenerateRoads(self,block):
		for outer_wall in block.get_outer_wall(block):
			if outer_wall:
				
				wlen = len(outer_wall)-1
				x = outer_wall[0][0]
				y = outer_wall[0][1]
				w = outer_wall[wlen][0]-x
				h = outer_wall[wlen][1]-y
				
				road = map_block.Block(x, y, w, h)
				road.scale(ROAD_SIZE-2, ROAD_SIZE-2)
				
				self.em.services["roads"].append(road)
				
				#TODO: check if road
				
				for i in range(road.w+1):
					for j in range(road.h+1):
						self.tiles[(road.x+i,road.y+j)].model = libtcod.CHAR_BLOCK3
						self.tiles[(road.x+i,road.y+j)].color = libtcod.darker_yellow
						self.tiles[(road.x+i,road.y+j)].road = True
	
		pass
	#-------------------------------------------------------------------
	def PlaceWall(self,x,y):
		self.tiles[(x,y)].blocked = True
		self.tiles[(x,y)].block_sight = True
		
	def ClearWall(self,x,y):
		self.tiles[(x,y)].blocked = False
		self.tiles[(x,y)].block_sight = False
		
	def update_fov_map(self):
		'''fov_map = self.em.services["fov"].fov_map
		for y in xrange(self.h):
			for x in xrange(self.w):
				
				#(i,j) = self.em.services["render"].viewport.cord_view(x,y)
				(i,j) = (x,y)
				
				
				libtcod.map_set_properties(fov_map, i, j,
						not self.tiles[(x,y)].block_sight,
						not self.tiles[(x,y)].blocked)'''
		
		self.em.services["fov"].update_map()
					
	def get_objects_in_radius(self,x,y,radius):
		objects = []
		for obj in self.em.services["objects"]:
			square_dist = (x - obj.x) ** 2 + (y - obj.y) ** 2
			if square_dist <= radius ** 2:
				#obj.model = "?"
				objects.append(obj)
				
		return objects
	#-------------------------------------------------------------------
	#	check if player crossed chunks border and precache/unload new chunks
	#-------------------------------------------------------------------	
		
	def e_on_player_move(self, _from, _to):	
					
		chunk_x = _from.x / CHUNK_SIZE
		chunk_y = _from.y / CHUNK_SIZE
		chunk_to_x = _to.x / CHUNK_SIZE
		chunk_to_y = _to.y / CHUNK_SIZE
		
		dx = chunk_to_x - chunk_x
		dy = chunk_to_y - chunk_y
					
	#-------------------------------------------------------------------
	def notify(self,event):
		if isinstance(event, events.EntMoveEvent):
			#IF ENTITY IS MOVING, MARK TILES AS BLOCKED/UNBLOCKED FOR FAST PATH SEARCH
			
			from_tile = self.tiles[(event._from.x,event._from.y)]
			to_tile = self.tiles[(event._to.x,event._to.y)]
			#if not self.is_blocked(event._from.x,event._from.y):
			if event.entity.obstacle:
				from_tile.obstacle = False
				to_tile.obstacle = True	
				
			from_tile.objects.remove(event.entity)
			to_tile.objects.append(event.entity)
			event.entity.tile = to_tile
			
			if isinstance(event.entity, player.PlayerEnt):
				self.e_on_player_move(event._from,event._to)
				
		if isinstance(event, events.EntSpawnEvent):
			spawn_tile = self.tiles[(event.spawn.x,event.spawn.y)]
			if event.entity.obstacle:
				spawn_tile.obstacle = True
				
			spawn_tile.objects.append(event.entity)
			
		if isinstance(event, events.EntDieEvent):
			destroy_tile = self.tiles[(event.entity.x,event.entity.y)]
			if event.entity.obstacle:
				destroy_tile.obstacle = False
				
		if isinstance(event, events.EntDestroyEvent):
			destroy_tile = self.tiles[(event.entity.x,event.entity.y)]
			if event.entity.obstacle:
				destroy_tile.obstacle = False
				
			destroy_tile.objects.remove(event.entity)
			self.em.services["objects"].remove(event.entity)
				
		if isinstance(event, events.SuspiciousSoundEvent):
			objects = self.get_objects_in_radius(event.origin.x, event.origin.y, event.radius)
			for obj in objects:
				obj.notify(event)
				
				
		#NOTIFY EVERY NPC NEAR THE CRIMESCENE OF MURDER		
		if isinstance(event, events.CriminalAction):
			objects = self.em.services["objects"]
			fov_map = self.em.services["fov_map"]	
			
			#notify every object in player's FOV as an eyewitness
			#TODO: actualy, replace player's fov radius with npc fov radius
			
			for obj in objects:
				if self.em.services["fov"].tile_visible(obj.x, obj.y):
					obj.notify(
						events.NPCWitnessCrimeEvent(
							event.origin.x,
							event.origin.y
						)
					)
